#!/usr/bin/env python

"""
 Converts a Pyjon parse tree into JooScript syntax.

 Created in Jan 2011 by Evgen Burzak <buzzilo@gmail.com>
"""

unit_obj = "unit"
root_obj = "root"
glob_obj = "glob"
class_obj = "__class__"
group_obj = "__group__"
none = "none"

import sys, copy
from pyjon import node2js, jsparser
import jooparser, js_node2joo, defs

joo = jooparser.StaticContext
NodeToJS = node2js.NodeToJS

class JooNodeToJS(NodeToJS): 

    def __init__(self, parent=None): 
        NodeToJS.__init__(self, parent)
        self.parentContext = parent

    def convert_class(self,n,i,c):
        #self.check(n,attrs=["classForm","ancestors","body"], 
                     #optattrs=["namespace","name","role"])
        ctx = JooClass(self, n)
        return ctx.convert_class(i,c)

    def convert_package(self,n,i,c):
        #self.check(n,attrs=["group","body"])
        ctx = JooPack(self, n)
        return ctx.convert_package(i, c)

    def convert_let(self,n,i,c):
        n.value = "var"
        return NodeToJS.convert_var(self,n,i,c)

    def convert_when(self,n,i,c):
        self.check(n, attrs=["eventType", "eventHandler"], 
                      optattrs=["useCapture", "eventThread"])
        return "when ..."

    def convert_dispatch(self,n,i,c):
        self.check(n, attrs=["eventType"], optattrs=["eventThread"])
        return "%s ..." % n.value

    convert_dismiss = convert_dispatch

    convert_on = convert_once = convert_when

    def convert_function(self,n,i,c):
        #self.check(n,attrs=["functionForm","params","body"], optattrs=["name"])
        ctx = JooFunc(self,n)
        return ctx.convert_function(i,c)

    def convert_script(self,n,i,c):
        self.check(n, optattrs=["clsDecls", "roleDecls"])
        if n.isRootNode:
            ctx = JooUnit(self, n)
            return ctx.convert_script(i, c)

        return NodeToJS.convert_script(self,n,i,c)

    def convert_import(self,n,i,c):
        self.check(n,attrs=["groups"])
        groups = ",".join(["%r" % ".".join(x) for x in n.groups])
        return "%s.Import(%s)" % (unit_obj, groups)

    def convert_none(self,n,i,c):
        return n.value

    convert_self = \
    convert_unit = \
    convert_root = \
    convert_glob = convert_none

    def convert_bitwise_or(self, n, i, c):
        n.value = "|"
        return NodeToJS.convert_plus(self,n,i,c)

    def convert_strict_eq(self, n, i, c):
        n.value = "==="
        return NodeToJS.convert_plus(self,n,i,c)

    def convert_identifier(self, n, i, c):
        self.check(n,optattrs=["initializer","name","readOnly","varType"])
        if hasattr(n,"initializer"):
            return "%s=%s" % (n.value, self.o(n.initializer, i, c))
        elif hasattr(n,"name"):
            assert n.name == n.value
            return self.context_var(n, i, c)
        else:
            return str(n.value)

    def convert_role(self, n, i, c):
        self.check(n,optattrs=["roleName", "roleForm"])
        ctx = JooRole(self,n)
        return ctx.convert_role(i,c)

    def convert_use(self, n, i, c):
        return '"use %s"' % n.value

    def convert_hashmap_init(self, n, i, c):
        self.check(n, subnodes=len(n))
        table = []
        i = 0
        for x in n:
            if x.type == "HASHMAP_ENTRY" and len(x) == 2:
                table.append("[%s,%s]" % (self.o(x[0],i,c), self.o(x[1],i,c)))
            else:
                table.append("[%s,%s]" % (i, self.o(x[0],i,c)))
            i += 1

        return '%s.$([%s])' % (unit_obj, ",".join(table))

    def convert_hash_seek(self, n, i, c):
        self.check(n, subnodes=2)
        return "%s.$(%s)" % (self.o(n[0],i,c), self.o(n[1],i,c))

    #def convert_hashmap_entry(self, n, i, c):
        #self.check(n, subnodes=2)
        #return "[%s,%s]" % (self.o(n[0],i,c), self.o(n[1],i,c))

    def context_var(self, n, i, c):
        if n.type == "IDENTIFIER":
            global_var = n.value[0] == "$"
            if not global_var:
                var = n.value in defs.deprecatedKeywords and ('["%s"]' % n.value) or ("."+n.value) 
                return "this" + var
            else:
                return str(n.value)
        else:
            return self.o(n,i,c)

    def convert_assign(self, n, i, c):
        self.check(n, subnodes=2, attrs=["assignOp"], 
                optattrs=["namespace"])
        var = self.context_var(n[0], i, c)
        if n.assignOp:
            assign = "%s=" % defs.tokens[n.assignOp]
        else:
            assign = "="
        return "%s%s%s" % (var, assign, self.o(n[1],i,c))
        #return NodeToJS.convert_assign(self,n,i,c)
        

class JooUnit(JooNodeToJS): 
    """
    Unit context
    """
    def __init__(self, parentContext, unit): 
        JooNodeToJS.__init__(self, parentContext)
        self.unit = unit

    def convert_script(self, i, c):
        return "$Unit(%r,%r,function(%s)%s);" % ("<path>","<file>",
                ",".join((unit_obj,root_obj,glob_obj,none)), 
                NodeToJS.convert_script(self,self.unit,i,c))


class JooRole(JooNodeToJS): 
    """
    Role
    """
    def __init__(self, parentContext, role): 
        JooNodeToJS.__init__(self, parentContext)
        self.role = role
        self.name = getattr(role, "name", None)

    def convert_role(self,i,c):
        self.check(self.role, optattrs=["name"], subnodes=len(self.role))
        return "%sRole({%s})" % (self.name and "var %s=" % self.name, 
                ",".join([self.o(x,i,c) for x in self.role]))

    def convert_role_entry(self,n,i,c):
        self.check(n, attrs=["name","namespace"],
                    optattrs=["constructor", "propertyType"])
        propType = str(getattr(n, "propertyType", None))
        if propType.upper() in js_node2joo.type_.keys():
            propType = js_node2joo.type_[propType.upper()]
        fields = ["type:"+str(propType)]
        if getattr(n, "constructor", None):
            fields.append("constructor:true")
        for field, true in n.namespace.iteritems():
            fields.append("%s:%s" % (field, true and "true" or "false"))

        if len(fields) > 1:
            return "%s:{%s}" % (n.name, ",".join(fields))
        else:
            return "%s:%s" % (n.name, propType)

class JooFunc(JooNodeToJS): 
    """
    Converts a parsed function node into JS
    """
    def __init__(self, parentContext, fun): 
        JooNodeToJS.__init__(self, parentContext)
        self.fun = fun
        self.body = fun.body
        self.name = getattr(fun, "name", "")
        self.params = fun.params
        self.form = fun.functionForm

    def convert_function(self,i,c):
        self.check(self.fun, attrs=["functionForm","params","body"],
                        optattrs=["constructor","name","namespace","restParams","retType"])
        #self.check(n,attrs=["functionForm","params","body"], optattrs=["name"])
        funBody = self.o(self.body,i,c)
        if self.form == jsparser.DECLARED_FORM:
            return "function %s(%s) %s" % (self.name,
                    ", ".join(param for param in self.params),
                    funBody)
        else:
            #if n.functionForm == joo.LAMBDA_FORM:
                #funBody = "{return %s}" % funBody
            return "function %s(%s)%s" % (self.name, ", ".join(self.params), funBody)


class JooPack(JooNodeToJS): 
    """
    Converts a package node into JS
    """
    def __init__(self, parentContext, pack): 
        JooNodeToJS.__init__(self, parentContext)
        self.pack = pack
        self.group = pack.group
        self.body = pack.body

    def convert_assign(self, n, i, c):
        self.check(n,optattrs=["namespace"])
        return JooNodeToJS.convert_assign(self,n,i,c)

    def convert_package(self, i, c):
        self.check(self.pack, attrs=["group","body"])
        group = ".".join(self.group)
        return "%s.Package(%r,function(%s)%s)" % (unit_obj, group, group_obj, 
                self.o(self.body,i,c))


class JooClass(JooNodeToJS): 
    """
    Converts a parsed class node into JS
    """
    def __init__(self, parentContext, class_): 
        JooNodeToJS.__init__(self, parentContext)
        self.namespace = []
        self.class_ = class_
        self.name = getattr(class_, "name", None)

    def convert_assign(self, n, i, c):
        self.check(n, attrs=["assignOp"], optattrs=["namespace"], subnodes=2)

    def convert_class(self, i, c):
        self.check(self.class_, attrs=["classForm","body"], 
                     optattrs=["name","role","namespace","ancestors"])

        return "%s.buildClass(%s,%s,%s,%s)" % (unit_obj, 
                self.name and ("%r" % self.name) or none,
                "","","")

def convert(parsetree, include_props=False):
    js = JooNodeToJS()
    return js.convert(parsetree, "", {}) + "\n"
